/* ---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

// 部分代码来源并修改自：
// - https://github.com/microsoft/vscode/blob/1.63.0/src/vs/platform/terminal/node/terminalEnvironment.ts
// - https://github.com/microsoft/vscode/blob/ff383268424b1d4b6620e7ea197fb13ae513414f/src/vs/base/node/shell.ts

import fs from 'fs';
import { release, userInfo } from 'os';
import {
  IProcessEnvironment,
  isLinux,
  isMacintosh,
  isWindows,
  OperatingSystem,
} from '../../utils';
import path from 'path';

// Windows 下常见的 Git Bash 路径
export const WINDOWS_GIT_BASH_PATHS = [
  `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`,
  `${process.env['ProgramW6432']}\\Git\\usr\\bin\\bash.exe`,
  `${process.env['ProgramFiles']}\\Git\\bin\\bash.exe`,
  `${process.env['ProgramFiles']}\\Git\\usr\\bin\\bash.exe`,
  `${process.env['LocalAppData']}\\Programs\\Git\\bin\\bash.exe`,
  `${process.env['UserProfile']}\\scoop\\apps\\git-with-openssh\\current\\bin\\bash.exe`,
  `${process.env['AllUsersProfile']}\\scoop\\apps\\git-with-openssh\\current\\bin\\bash.exe`,
];

// 获取 PowerShell 可执行文件路径列表
export async function getPowershellPaths() {
  const paths: string[] = [];
  // TODO: 引入 VSCODE 查找逻辑
  // Add all of the different kinds of PowerShells
  // for await (const pwshExe of enumeratePowerShellInstallations()) {
  //   paths.push(pwshExe.exePath);
  // }
  paths.push('powershell.exe');
  return paths;
}

// 判断文件是否存在，存在则返回路径，否则返回 undefined
export const exists = async (p: string) => {
  try {
    await fs.promises.access(path.normalize(p));
    return p;
  } catch (_e) {
    return;
  }
};

// 异步查找候选 shell 可执行文件，返回第一个存在的路径
export async function findShellExecutableAsync(
  candidate: string[]
): Promise<string | undefined> {
  if (candidate.length === 0) {
    return undefined;
  }
  // 返回第一个存在的可执行文件
  return Promise.all(candidate.map((v) => findExecutable(v))).then((v) =>
    v.find(Boolean)
  );
}

/**
 * @deprecated use findShellExecutableAsync
 */
// 同步查找候选 shell 可执行文件，返回第一个存在的路径
export function findShellExecutable(candidate: string[]): string | undefined {
  if (candidate.length === 0) {
    return undefined;
  }

  for (const p of candidate) {
    if (fs.existsSync(path.normalize(p))) {
      return p;
    }
    continue;
  }
}

// 获取对象中 key（不区分大小写）对应的值
export function getCaseInsensitive(
  target: Record<string, any>,
  key: string
): any {
  const lowercaseKey = key.toLowerCase();
  const equivalentKey = Object.keys(target).find(
    (k) => k.toLowerCase() === lowercaseKey
  );
  return equivalentKey ? target[equivalentKey] : target[key];
}

// 查找可执行文件的绝对路径
export async function findExecutable(
  command: string,
  cwd?: string,
  paths?: string[],
  env: IProcessEnvironment = process.env as IProcessEnvironment
): Promise<string | undefined> {
  // 如果是绝对路径，直接判断是否存在
  if (path.isAbsolute(command)) {
    return (await exists(command)) ? command : undefined;
  }
  if (cwd === undefined) {
    cwd = process.cwd();
  }
  const dir = path.dirname(command);
  if (dir !== '.') {
    // 如果带有相对目录，则拼接为绝对路径
    const fullPath = path.join(cwd, command);
    return (await exists(fullPath)) ? fullPath : undefined;
  }
  const envPath = getCaseInsensitive(env, 'PATH');
  if (paths === undefined && typeof envPath === 'string') {
    paths = envPath.split(path.delimiter);
  }
  // 没有 PATH 环境变量，直接拼接 cwd
  if (paths === undefined || paths.length === 0) {
    const fullPath = path.join(cwd, command);
    return (await exists(fullPath)) ? fullPath : undefined;
  }
  // 遍历 PATH 路径，查找可执行文件
  for (const pathEntry of paths) {
    // pathEntry 是绝对路径
    let fullPath: string;
    if (path.isAbsolute(pathEntry)) {
      fullPath = path.join(pathEntry, command);
    } else {
      fullPath = path.join(cwd, pathEntry, command);
    }

    if (await exists(fullPath)) {
      return fullPath;
    }
    if (isWindows) {
      // Windows 下尝试 .com 和 .exe 后缀
      let withExtension = fullPath + '.com';
      if (await exists(withExtension)) {
        return withExtension;
      }
      withExtension = fullPath + '.exe';
      if (await exists(withExtension)) {
        return withExtension;
      }
    }
  }
  const fullPath = path.join(cwd, command);
  return (await exists(fullPath)) ? fullPath : undefined;
}

// 获取 Windows 版本号
export function getWindowsBuildNumber(): number {
  const osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(release());
  let buildNumber = 0;
  if (osVersion && osVersion.length === 4) {
    buildNumber = parseInt(osVersion[3], 10);
  }
  return buildNumber;
}

/**
 * 获取系统默认 shell（不是 VS Code 终端默认 shell）
 * @param os 操作系统类型
 */
export async function getSystemShell(os: OperatingSystem): Promise<string> {
  if (os === OperatingSystem.Windows) {
    if (isWindows) {
      return getSystemShellWindows();
    }
    // 非 Windows 系统不检测 Windows shell
    return getWindowsShell();
  }
  return getSystemShellUnixLike(os);
}

let _TERMINAL_DEFAULT_SHELL_UNIX_LIKE: string | null = null;

// 获取类 Unix 系统默认 shell
function getSystemShellUnixLike(
  os: OperatingSystem,
  env = process.env
): string {
  // 只对当前 OS 使用 $SHELL
  if (
    (isLinux && os === OperatingSystem.Macintosh) ||
    (isMacintosh && os === OperatingSystem.Linux)
  ) {
    return '/bin/bash';
  }

  if (!_TERMINAL_DEFAULT_SHELL_UNIX_LIKE) {
    let unixLikeTerminal: string | undefined;

    unixLikeTerminal = env['SHELL'];

    if (!unixLikeTerminal) {
      try {
        // $SHELL 可能未设置，尝试读取 /etc/passwd
        // Node 文档：如果用户没有用户名或主目录会抛出 SystemError
        const info = userInfo();
        if (info.shell) {
          unixLikeTerminal = info.shell;
        }
      } catch (err) {}
    }

    if (!unixLikeTerminal) {
      unixLikeTerminal = 'sh';
    }

    // 某些系统 $SHELL 为 /bin/false，会导致终端不可用
    if (unixLikeTerminal === '/bin/false') {
      unixLikeTerminal = '/bin/bash';
    }

    _TERMINAL_DEFAULT_SHELL_UNIX_LIKE = unixLikeTerminal;
  }
  return _TERMINAL_DEFAULT_SHELL_UNIX_LIKE;
}

// 获取 Windows shell 路径
export function getWindowsShell(env = process.env): string {
  return env['comspec'] || 'cmd.exe';
}

let _TERMINAL_DEFAULT_SHELL_WINDOWS: string | null = null;

// 获取 Windows 系统默认 shell 路径
async function getSystemShellWindows(env = process.env): Promise<string> {
  if (!_TERMINAL_DEFAULT_SHELL_WINDOWS) {
    const isAtLeastWindows10 = isWindows && parseFloat(release()) >= 10;
    const is32ProcessOn64Windows = Object.prototype.hasOwnProperty.call(
      env,
      'PROCESSOR_ARCHITEW6432'
    );
    const powerShellPath = `${env['windir']}\\${
      is32ProcessOn64Windows ? 'Sysnative' : 'System32'
    }\\WindowsPowerShell\\v1.0\\powershell.exe`;
    _TERMINAL_DEFAULT_SHELL_WINDOWS = isAtLeastWindows10
      ? powerShellPath
      : getWindowsShell(env);
    return _TERMINAL_DEFAULT_SHELL_WINDOWS;
  }
  return _TERMINAL_DEFAULT_SHELL_WINDOWS;
}
